package com.quest.completableFuture;

import com.quest.ThreadDemoApplication;
import com.sun.javafx.geom.Vec2d;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * Created by Gansm on 2018-08-22 0022.
 */
//@RunWith(SpringRunner.class)
//@SpringBootTest(classes = ThreadDemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoTest {
    /**
     *@Description: CompletableFuture基本用法，异步阻塞
     */
    @Test
    public void test() throws ExecutionException, InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        CompletableFuture<Integer> c = new CompletableFuture<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("模拟耗时……");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通知CompletableFuture，任务已经完成
                c.complete(1);
            }
        }).start();
        //获取任务结果，没有完成会一直阻塞
        Integer i = c.get();
        watch.stop();
        System.out.println("获取结果：" + i + " 总耗时：" + watch.getTime() + "ms");
    }
    /**
     *@Description: CompletableFuture工厂方法，
     * supplyAsync 方法接受一个生产者（Supplier）作为参数，返回一个 CompletableFuture
    对象。生产者方法会交由 ForkJoinPool池中的某个执行线程（ Executor ）运行，但是你也可以使用 supplyAsync 方法的重载版本，传递第二个参数指定线程池执行器执行生产者方法。
     */
    @Test
    public void test2() throws ExecutionException, InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        CompletableFuture<String> c = CompletableFuture.supplyAsync(()->{
            System.out.println("模拟耗时……");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result";
        });
        watch.stop();
        System.out.println("获取结果：" + c.get() + " 总耗时：" + watch.getTime() + "ms");
    }
    /**
     *@Description: allOf 工厂方法接收一个由CompletableFuture 构成的数组，数组中的所有 Completable-Future 对象执行完成之后，
     * 它返回一个 CompletableFuture<Void> 对象。
     * 这意味着，如果你需要等待多个 CompletableFuture 对象执行完毕，对 allOf 方法返回的
       CompletableFuture 执行 join 操作可以等待CompletableFuture执行完成。

    或者你可能希望只要 CompletableFuture 对象数组中有任何一个执行完毕就不再等待，在这种情况下，你可以使用一个类似的工厂方法 anyOf 。
     */
    @Test
    public void test3() throws ExecutionException, InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        CompletableFuture<String> c1 = CompletableFuture.supplyAsync(()->{
            System.out.println("task1 doing ...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result1";
        });
        CompletableFuture<String> c2 = CompletableFuture.supplyAsync(()->{
            System.out.println("task2 doing ...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result2";
        });

        CompletableFuture<Object> anyResult = CompletableFuture.anyOf(c1, c2);
        watch.suspend();
        System.out.println("first task over: "+anyResult.get()+" ,耗时：" + watch.getTime() + "ms");
        watch.resume();
        CompletableFuture<Void> allResult = CompletableFuture.allOf(c1, c2);
        //阻塞所有任务执行完成
        allResult.join();
        watch.stop();
        System.out.println("获取结果：" + allResult.get() + " 总耗时：" + watch.getTime() + "ms");
    }

    /**
     *@Description: thenCompose 方法允许你对两个异步操作进行流水线，第一个操作完成时，将其结果作为参数传递给第二个操作。
     * 你可以创建两个CompletableFutures 对象，对第一个 CompletableFuture 对象调用thenCompose ，并向其传递一个函数。
     * 当第一个CompletableFuture 执行完毕后，它的结果将作为该函数的参数，
     * 这个函数的返回值是以第一个 CompletableFuture 的返回做输入计算出的第二个 CompletableFuture 对象。
     */
    @Test
    public void test4() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        CompletableFuture<String> c1 = CompletableFuture.supplyAsync(()->{
            System.out.println("task1 doing ...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "200";
        });
        CompletableFuture<String> c2 = c1.thenCompose(result->CompletableFuture.supplyAsync(()->{
            System.out.println("task2 doing ...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result+" is Success!";
        }));
        System.out.println(c2.get());
        System.out.println("总耗时："+(System.currentTimeMillis()-start)+"ms");
    }

    /**
     *@Description: 另一种比较常见的情况是，你需要将两个完
    全不相干的 CompletableFuture 对象的结果整合起来，而且你也不希望等到第一个任务完全结
    束才开始第二项任务。

    这种情况，你应该使用 thenCombine 方法，它接收名为 BiFunction 的第二参数，这个参数
    定义了当两个 CompletableFuture 对象完成计算后，结果如何合并。
     */
    @Test
    public void test5() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        CompletableFuture<Integer> c1 = CompletableFuture.supplyAsync(()->{
            System.out.println("task1 doing ...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 200;
        });
        CompletableFuture<Integer> c2 = c1.thenCombine(
            CompletableFuture.supplyAsync(()->{
                System.out.println("task1 doing ...");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 300;
            }),
            //合并函数
            (result1,result2)->result1+result2);

        System.out.println(c2.get());
        System.out.println("总耗时："+(System.currentTimeMillis()-start)+"ms");
    }

    /**
     *@Description: 响应 CompletableFuture 的 completion 事件
    我们可以在每个CompletableFuture 上注册一个操作，该操作会在 CompletableFuture 完成执行后调用它。
    CompletableFuture 通过 thenAccept 方法提供了这一功能，它接收CompletableFuture 执行完毕后的返回值做参数。
     */
    @Test
    public void test6() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        CompletableFuture<Integer> c1 = CompletableFuture.supplyAsync(()->{
            System.out.println("task1 doing ...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 200;
        });
        c1.thenAccept(result ->System.out.println("task1 done,result:" + result+", 耗时："+(System.currentTimeMillis() - start)+"ms"));

        CompletableFuture<Integer> c2 = CompletableFuture.supplyAsync(()->{
            System.out.println("task2 doing ...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 300;
        });
        c2.thenAccept(result -> System.out.println("task2 done,result:" + result+", 耗时："+(System.currentTimeMillis() - start)+"ms"));
        CompletableFuture<Integer> c3 = c1.thenCombine(c2,
                //合并函数
                (r1, r2)->{
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return r1 + r2;
                });
        System.out.println(c3.get()+", 总耗时："+(System.currentTimeMillis() - start)+"ms");
    }

    @Test
    public void test7(){

    }

}
